package model

import (
	"bytes"
	_ "embed"
	"gitee.com/whilew/gen-go/pkg/resource"
	"io"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

//go:embed gen_model.go.tmpl
var defaultTemplateFile []byte

//go:embed gen_model_split.go.tmpl
var splitTemplateFile []byte

// ModelTemplateVisitor is a visitor that generates a model file
type ModelTemplateVisitor struct {
	t       *template.Template
	tData   interface{}
	genDir  string // 生成目录 默认为当前目录
	isSplit bool   // 是否分割生成model文件
	err     error
}

func newModelTemplateVisitor() *ModelTemplateVisitor {
	v := &ModelTemplateVisitor{}
	return v
}
func (v *ModelTemplateVisitor) WithGenDir(genDir string) *ModelTemplateVisitor {
	v.genDir = genDir
	return v
}
func (v *ModelTemplateVisitor) WithTemplateFile(filePath string) *ModelTemplateVisitor {
	var tempFileData []byte
	f, err := os.Open(filePath)
	if err != nil {
		v.err = err
		return v
	}
	defer f.Close()
	tempFileData, v.err = io.ReadAll(f)
	if v.err != nil {
		return v
	}
	v.setTemplate(tempFileData)
	return v
}
func (v *ModelTemplateVisitor) WithSplit() *ModelTemplateVisitor {
	v.isSplit = true
	return v
}
func (v *ModelTemplateVisitor) setTemplate(tempFileData []byte) *ModelTemplateVisitor {
	if v.err != nil {
		return v
	}
	t, err := template.New("gen_model.go.tmpl").
		Funcs(template.FuncMap{
			"toTitle": func(s string) string {
				ss := strings.Split(s, "_")
				str := ""
				for _, v := range ss {
					str += strings.ToUpper(v[:1]) + v[1:]
				}
				return str
			},
			"toUpper": func(s string) string {
				return strings.ToUpper(s)
			},
			"getTag": func(field *resource.FieldInfo, key string) string {
				return field.Tag.Get(key)
			},
		}).
		Parse(string(tempFileData))
	v.t = t
	v.err = err
	return v
}

func (v *ModelTemplateVisitor) Visit() resource.VisitorFunc {
	if v.t == nil {
		if v.isSplit {
			v.setTemplate(splitTemplateFile)
		} else {
			v.setTemplate(defaultTemplateFile)
		}
	}
	if v.genDir == "" {
		var err error
		v.genDir, err = filepath.Abs("./")
		if err != nil {
			v.err = err
		}
	}
	return func(info *resource.Info, err error) error {
		if v.err != nil {
			return v.err
		}
		if err != nil {
			return err
		}
		if v.isSplit {
			for _, iv := range info.Models {
				f := &resource.FileInfo{
					Path:    filepath.Join(v.genDir, iv.Name+".go"),
					Content: bytes.NewBuffer(nil),
				}
				err = v.t.Execute(f, iv)
				if err != nil {
					return err
				}
				info.Files = append(info.Files, f)
			}
			return nil
		}
		f := &resource.FileInfo{
			Path:    filepath.Join(v.genDir, "gen_model.go"),
			Content: bytes.NewBuffer(nil),
		}
		err = v.t.Execute(f, info)
		if err != nil {
			return err
		}
		info.Files = append(info.Files, f)
		return nil
	}
}

func GenModelByDefaultTemplateFile(filePath string) resource.VisitorFunc {
	return func(info *resource.Info, err error) error {
		t, err := template.New("gen_model.go.tmpl").
			Funcs(template.FuncMap{
				"toTitle": func(s string) string {
					ss := strings.Split(s, "_")
					str := ""
					for _, v := range ss {
						str += strings.ToUpper(v[:1]) + v[1:]
					}
					return str
				},
				"toUpper": func(s string) string {
					return strings.ToUpper(s)
				},
			}).
			Parse(string(defaultTemplateFile))
		if err != nil {
			return err
		}
		f := &resource.FileInfo{
			Path:    filePath,
			Content: bytes.NewBuffer(nil),
		}
		err = t.Execute(f, info)
		if err != nil {
			return err
		}
		info.Files = append(info.Files, f)
		return nil
	}
}
func GenModelBySplitTemplateFile(dir string) resource.VisitorFunc {
	return func(info *resource.Info, err error) error {
		t, err := template.New("gen_model_split.go.tmpl").
			Funcs(template.FuncMap{
				"toTitle": func(s string) string {
					ss := strings.Split(s, "_")
					str := ""
					for _, v := range ss {
						str += strings.ToUpper(v[:1]) + v[1:]
					}
					return str
				},
				"toUpper": func(s string) string {
					return strings.ToUpper(s)
				},
			}).
			Parse(string(splitTemplateFile))

		for _, v := range info.Models {
			f := &resource.FileInfo{
				Path:    filepath.Join(dir, v.Name+".go"),
				Content: bytes.NewBuffer(nil),
			}
			err = t.Execute(f, v)
			if err != nil {
				return err
			}
			info.Files = append(info.Files, f)
		}
		return nil
	}
}
